package com.gitee.wsl.mathematics.geometry.d2.contour.segment.ext

import com.gitee.wsl.mathematics.geometry.d2.contour.segment.CurveSegmentShape2D
import com.gitee.wsl.mathematics.geometry.d2.contour.segment.Segment2D
import com.gitee.wsl.mathematics.vector.vec2.Vec2f
import kotlin.math.abs


fun Segment2D.splitOnExtrema(): List<CurveSegmentShape2D<Float, Vec2f>> {
    var extrema = extrema().toMutableList()

    if (isStraight(0.05)) {
        return listOf(this)
    }

    if (simple && extrema.isEmpty()) {
        return listOf(this)
    }

    if (extrema.isEmpty()) {
        return listOf(this)
    }
    if (extrema[0] <= 0.01) {
        extrema[0] = 0.0f
    } else {
        extrema = (mutableListOf(0.0f) + extrema).toMutableList()
    }

    if (extrema.last() < 0.99) {
        extrema = (extrema + listOf(1.0f)).toMutableList()
    } else if (extrema.last() >= 0.99) {
        extrema[extrema.lastIndex] = 1.0f
    }

    return extrema.zipWithNext().map {
        sub(it.first.toDouble(), it.second.toDouble())
    }
}

fun CurveSegmentShape2D<Float, Vec2f>.splitToSimple(step: Double): List<CurveSegmentShape2D<Float, Vec2f>> {
    var t1 = 0.0
    var t2 = 0.0
    val result = mutableListOf<CurveSegmentShape2D<Float, Vec2f>>()
    while (t2 <= 1.0) {
        t2 = t1 + step
        while (t2 <= 1.0 + step) {
            val segment = sub(t1, t2)
            if (!segment.simple) {
                t2 -= step
                if (abs(t1 - t2) < step) {
                    return listOf(this)
                }
                val segment2 = sub(t1, t2)
                result.add(segment2)
                t1 = t2
                break
            }
            t2 += step
        }

    }
    if (t1 < 1.0) {
        result.add(sub(t1, 1.0))
    }
    if (result.isEmpty()) {
        result.add(this)
    }
    return result
}

fun CurveSegmentShape2D<Float, Vec2f>.splitAtBase(
    ascendingTs: List<Double>,
    weldEpsilon: Double = 1E-6
): List<CurveSegmentShape2D<Float, Vec2f>> {
    if (ascendingTs.isEmpty()) {
        return listOf(this)
    }

    @Suppress("NAME_SHADOWING")
    val ascendingTs = (listOf(0.0) + ascendingTs + listOf(1.0)).weldAscending(weldEpsilon)
    return ascendingTs.windowed(2, 1).map {
        sub(it[0], it[1])
    }
}

fun Segment2D.splitAt(ascendingTs: List<Double>,
                      weldEpsilon: Double = 1E-6) : List<Segment2D> {
    @Suppress("UNCHECKED_CAST")
    return splitAtBase(ascendingTs, weldEpsilon) as List<Segment2D>
}


/**
 * Weld values if their distance is less than [epsilon]
 */
fun List<Double>.weldAscending(epsilon: Double = 1E-6): List<Double> {
    return if (size <= 1) {
        this
    } else {
        val result = mutableListOf(first())
        var lastPassed = first()
        for (i in 1 until size) {
            require(this[i] >= lastPassed) { "input list is not in ascending order" }
            if (this[i] - lastPassed > epsilon) {
                result.add(this[i])
                lastPassed = this[i]
            }
        }
        result
    }
}
